package com.hydra.base.utils;

import com.hydra.base.common.HttpClientResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 网络请求工具
 * @author Hydra
 */
@Slf4j
public class HttpUtils {
	
	// 编码格式。发送编码格式统一用UTF-8
	private static final String ENCODING = "UTF-8";
	
	// 设置连接超时时间，单位毫秒。
	private static final int CONNECT_TIMEOUT = 6000;
	
	// 请求获取数据的超时时间(即响应时间)，单位毫秒。
	private static final int SOCKET_TIMEOUT = 6000;

	// 从连接池获取连接超时时间，单位毫秒。
	private static final int CONNECT_REQUEST_TIMEOUT = 3000;

	/**
	 * 解析url，其实也就是拼上斜杆
	 */
	public static String parseUrl(String baseUrl, String path){
		if(path.startsWith("http://") || path.startsWith("https://")){
			return path;
		}
		if(baseUrl.endsWith("/") && !path.startsWith("/")){
			path = "/" + path;
		}
		return baseUrl + path;
	}

	public static HttpClientResult doGet(String url) throws Exception {
		return doGet(url, null, null);
	}
	public static HttpClientResult doGet(String url, Map<String, String> headers, Map<String, String> params) throws Exception {

		// 创建http请求对象
		HttpGet httpGet = initGetMethod(url, headers, params);

		// 执行请求并获得响应结果
		return execute(httpGet);
	}
	
	private static HttpGet initGetMethod(String url, Map<String, String> headers, Map<String, String> params) throws Exception {
		// 创建访问的地址
		URIBuilder uriBuilder = new URIBuilder(url);
		if (params != null) {
			Set<Entry<String, String>> entrySet = params.entrySet();
			for (Entry<String, String> entry : entrySet) {
				uriBuilder.setParameter(entry.getKey(), entry.getValue());
			}
		}

		// 创建http对象
		HttpGet httpGet = new HttpGet(uriBuilder.build());
		/*
		  setConnectTimeout：设置连接超时时间，单位毫秒。
		  setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
		  超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
		  setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
		 */
		RequestConfig requestConfig = RequestConfig.custom()
				.setConnectTimeout(CONNECT_TIMEOUT)	//连接时间
				.setSocketTimeout(SOCKET_TIMEOUT)	//请求处理时间
				.setConnectionRequestTimeout(CONNECT_REQUEST_TIMEOUT)	//从连接池获取连接超时时间
				.build();
		httpGet.setConfig(requestConfig);
		
		// 设置请求头
		packageHeader(headers, httpGet);
		
		return httpGet;
	}

	/**
	 * 发送post请求；不带请求头和请求参数
	 * 
	 * @param url 请求地址
	 * @return  请求结果对象
	 * @throws Exception 网络异常
	 */
	public static HttpClientResult doPost(String url) throws Exception {
		return doPost(url, null, null);
	}
	
	/**
	 * 发送post请求；带请求参数
	 * 
	 * @param url 请求地址
	 * @param params 参数集合
	 * @return  请求结果对象
	 * @throws Exception 网络异常
	 */
	public static HttpClientResult doPost(String url, Map<String, String> params) throws Exception {
		return doPost(url, null, params);
	}

	/**
	 * 发送post请求；带请求头和请求参数
	 * 
	 * @param url 请求地址
	 * @param headers 请求头集合
	 * @param params 请求参数集合
	 * @return  请求结果对象
	 * @throws Exception 网络异常
	 */
	public static HttpClientResult doPost(String url, Map<String, String> headers, Map<String, String> params) throws Exception {
		// 创建http对象
		HttpPost httpPost = new HttpPost(url);
		/*
		  setConnectTimeout：设置连接超时时间，单位毫秒。
		  setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
		  超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
		  setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
		 */
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpPost.setConfig(requestConfig);
		// 设置请求头
		packageHeader(headers, httpPost);
		
		// 封装请求参数
		packageParam(params, httpPost);

		// 执行请求并获得响应结果
		return execute(httpPost);
	}
	
	/**
	 * Description: 获取网络图片的输入流
	 * @throws Exception	请求异常
	 */
	public static void getImageInputStream(String url, String path) throws Exception {
		getImageInputStream(url, path, null, null);
	}
	
	/**
	 * Description: 获取网络图片的输入流
	 * @param url	忘了文件的位置
	 * @param path	文件保存的目标路径
	 * @throws Exception	请求异常
	 */
	public static void getImageInputStream(String url, String path, Map<String, String> headers, Map<String, String> params) throws Exception {
		
		HttpGet httpGet = initGetMethod(url, headers, params);
		
		CloseableHttpClient client = HttpClients.createDefault();
		CloseableHttpResponse response = client.execute(httpGet);
		HttpEntity httpEntity = response.getEntity();

		if(httpEntity!=null){
			int statusCode = response.getStatusLine().getStatusCode();
			
			if(statusCode == 200) {
				InputStream is = httpEntity.getContent();
				//从流中读取文件并写入本地文件
				saveFileInputStream(is, path);
			}
			
		}
		response.close();
		client.close();
	}

	/**
	 * 从输入流中读取文件，并保存
	 * 注意：之后会关闭流
	 */
	public static void saveFileInputStream(InputStream is, String path) throws Exception {
		File file = new File(path);
		File parentFile = file.getParentFile();
		if(!parentFile.exists()){
			if(!parentFile.mkdirs()){
				throw new Exception("文件夹创建失败！"+parentFile.getAbsolutePath());
			}
		}
		try (OutputStream out = new FileOutputStream(file)) {
			int length;
			byte[] b = new byte[1024 * 10];
			while ((length = is.read(b)) != -1) {
				out.write(b, 0, length);
			}
		} catch (Exception e) {
			throw new Exception("下载文件异常, path:" + path, e);
		} finally {
			if (is != null) {
				is.close();
			}
		}
	}

	/**
	 * Description: 封装请求头
	 * @param params		入参
	 * @param httpMethod	method
	 */
	public static void packageHeader(Map<String, String> params, HttpRequestBase httpMethod) {
		/*httpPost.setHeader("Cookie", "");
		httpPost.setHeader("Connection", "keep-alive");
		httpPost.setHeader("Accept", "application/json");
		httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.9");
		httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");
		httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");*/
		// 封装请求头
//		httpMethod.setHeader("Referer", Constant.RootUrl);
		httpMethod.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=gb2312");
//		httpMethod.setHeader("Cookie", "yh=12B9772F784163B0C8A66304CF40F8F6E7CCB678E20B9351F18AC245B925CA055BD924670A1BA34179165E3BBFA05326");
		//伪装成google的爬虫
		httpMethod.setHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +https://www.google.com/bot.html)");
		if (params != null) {
			Set<Entry<String, String>> entrySet = params.entrySet();
			for (Entry<String, String> entry : entrySet) {
				// 设置到请求头到HttpRequestBase对象中
				httpMethod.setHeader(entry.getKey(), entry.getValue());
			}
		}
	}

	/**
	 * Description: 封装请求参数
	 * 
	 * @param params		入参
	 * @param httpMethod	method
	 * @throws UnsupportedEncodingException	请求异常
	 */
	public static void packageParam(Map<String, String> params, HttpEntityEnclosingRequestBase httpMethod)
			throws UnsupportedEncodingException {
		// 封装请求参数
		if (params != null) {
			List<NameValuePair> nvps = new ArrayList<>();
			Set<Entry<String, String>> entrySet = params.entrySet();
			for (Entry<String, String> entry : entrySet) {
				nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
			// 设置到请求的http对象中
			httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));
		}
	}

	/**
	 * Description: 获得响应结果
	 * @param httpMethod	http请求方式
	 * @throws Exception	请求异常
	 */
	public static HttpClientResult execute(HttpRequestBase httpMethod) throws Exception {
		// 创建httpClient对象
		CloseableHttpClient httpClient = HttpClients.createDefault();
		CloseableHttpResponse httpResponse = null;
		HttpClientResult result = new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR);
		try {
			// 执行请求
			httpResponse = httpClient.execute(httpMethod);

			// 获取返回结果
			if (httpResponse != null && httpResponse.getStatusLine() != null) {
				String content = "";
				if (httpResponse.getEntity() != null) {
					content = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
				}
				result = new HttpClientResult(httpResponse.getStatusLine().getStatusCode(), content);

				Header[] allHeaders = httpResponse.getAllHeaders();
				result.setAllHeaders(allHeaders);
			}
		} finally {
			//释放资源
			release(httpResponse);
			release(httpClient);
		}
		return result;
	}

	/**
	 * Description: 释放资源
	 */
	public static void release(Closeable closeable) throws IOException {
		// 释放资源
		if (closeable != null) {
			closeable.close();
		}
	}
	
}
